package com.why.server;

import com.why.codec.CommonDecoder;
import com.why.codec.CommonEncoder;
import com.why.handler.NettyServerHandler;
import com.why.hook.ServerLogOut;
import com.why.nacos.ServiceRegistry;
import com.why.provider.ServiceProvider;
import com.why.serializable.CommonSerializable;
import com.why.serializable.KroSerializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

/**
 * @program: why-rpc-framework
 * @description:
 * @author: @why
 * @create: 2021-03-15 16:00
 **/
@Slf4j
@Builder
@Getter
@AllArgsConstructor
public class NettyRpcServer implements Server {

    private int port;
    private String host;
    private ServiceProvider provider;
    private ServiceRegistry registry;
    private CommonSerializable serializable;



    private NettyRpcServer(){

    }

    @Override
    public void publishService(Object service, String serviceName) {
        //就是我们的提供者看看自己能够提供那些服务
        provider.toProvideServices(service);
        //接着将提供者能够提供的东西告诉注册中心
        log.info("去注册中心注册服务。。。");
        registry.register(serviceName, new InetSocketAddress(host, port));
        log.info("服务:{}:去nacos注册完成", serviceName);

    }

    @Override
    public void start() {
        if (serializable == null) {
            log.warn("未指定序列化器，使用默认序列化器");
            serializable = new KroSerializer();
        }
//老板搞里头
        EventLoopGroup boss = new NioEventLoopGroup();
        //员工搞里头
        EventLoopGroup worker = new NioEventLoopGroup();

        try {

            //启动类创建
            log.info("初始化netty");
            ServerBootstrap bootstrap = new ServerBootstrap();
            //指定老板和员工
            bootstrap
                    .group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    //选项介绍见博客分类的netty部分
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    //老板接到活后，把任务分给员工，此时员工应该做的事情
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //在管道上注册处理程序·
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new CommonDecoder());
                            pipeline.addLast(new CommonEncoder(serializable));
                            pipeline.addLast(new NettyServerHandler(provider));

                        }
                    });
            //同步绑定端口号。
            log.info("初始化完成");
            log.info("开始绑定端口号。。。");
            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("绑定端口号成功。。。");
            /**
             * 关闭的是所有的channel，如客户端的SocketChannel与服务器的Channel;
             *
             * 什么时候关闭，当客户端不再与服务器连接；
             *
             * 如QQ，有用户上线发送消息，下线以后SocketChannel就与服务器断开连接，这个时候就会关闭对应的SocketChannel，
             * 然后解除绑定关系；
             *
             * 若服务器关机重启，就会关闭服务器的Channel;然后关闭两个线程组bossGroup与workerGroup;
             *
             * 有连接就不关闭！
             *
             * 就是这行代码
             * .channel() 返回当前正在执行的通道
             */
            log.info("开始向jvm注册钩子。。。");
            ServerLogOut.logOut().clearAllService();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("RpcServer发生未知异常。。", e);
        } finally {
            //优雅的关闭netty的线程
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }


    }

}